function get(id) {
  return document.getElementById(id);
}

// Fills in innerHTML (or attribute) of an element by id
function fillIn(id, value, attr) {
  var el = get(id);

  if (!value) {
    el.style.display = 'none';
    return;
  }

  if (attr) {
    el[attr] = value;
  } else {
    el.innerHTML = value;
  }
}

// Because links from notifications apparently don't work in the way you would
// expect, we have to manually open a tab with a given URL.
function linkify(className, url, andClose) {
  if (!url) {
    return;
  }
  var links = document.getElementsByClassName(className);
  for (var i = 0; i < links.length; i++) {
    links[i].onclick = function(e) {
      chrome.tabs.create({url: url});
      if (andClose) {
        window.close();
      }
      e.stopPropagation();
    };
  }
}

// Seconds since epoch -> human readable date, e.g., "6 hours ago"
function hoursAgo(createdAt) {
  var now = new Date().getTime() / 1000;
  var diff = now - createdAt;
  var dayDiff = Math.floor(diff / 86400);
  if (diff < 0) {
    return 'in the future?';
  } else if (diff < 120) {
    return '1 minute ago';
  } else if (diff < 3600) {
    return Math.floor(diff / 60) + ' minutes ago';
  } else if (diff < 7200) {
    return '1 hour ago';
  } else if (diff < 86400) {
    return Math.floor(diff / 3600) + ' hours ago';
  } else if (dayDiff == 1) {
    return 'Yesterday';
  } else if (dayDiff < 7) {
    return dayDiff + ' days ago';
  } else if (dayDiff < 14) {
    return '1 week ago';
  } else {
    return Math.floor(dayDiff / 7) + ' weeks ago';
  }
}

// Makes an XHR request to the URL
function xhr(url, callback, opt_options) {
  getItem('token', function(settings) {
    var token = settings['token'];

    if (!token) return;

    url += (url.indexOf('?') >= 0) ? '&' : '?';
    url += 'oauth_token=' + token;

    url += '&v=20111220';

    var method = 'GET';
    if (opt_options && opt_options['method']) {
      method = opt_options['method'];
    }

    var xhr = new XMLHttpRequest();

    xhr.open(method, url, true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        if (xhr.status == 200) {
          callback(JSON.parse(xhr.responseText));
        } else {
          console.log('Error: ' + xhr.responseText);
        }
      }
    }
    xhr.send();
  });
}


// Comparator function by createdAt field, newest first
function byCreatedAt(a, b) {
  if (a['createdAt'] < b['createdAt']) {
    return 1;
  } else if (a['createdAt'] > b['createdAt']) {
    return -1;
  }
  return 0;
}

// Comparator function by createdAt field, oldest first
function byCreatedAtAscending(a, b) {
  return -byCreatedAt(a, b);
}

/**
 * Encode an object into a base-64 encoded JSON string.
 */
function encode(obj) {
  var str = JSON.stringify(obj);
  try {
    return window.btoa(str);
  } catch (err) {
    // It seems like btoa doesn't play nicely with Unicode, so based on
    // https://developer.mozilla.org/en/DOM/window.btoa#Unicode_Strings we have
    // to unescape+encode the Unicode parts, a process I've dubbed
    // 'extra-btoaing' -- when this is necessary we prepend a '-' and watch for
    // this in decode below.
    try {
      return '-' + window.btoa(unescape(encodeURIComponent(str)));
    } catch (err2) {
      throw err2;
    }
  }
}

/**
 * Decode a base-64 encoded JSON string into on object.
 */
function decode(str) {
  if (str[0] === '-') {
    return JSON.parse(decodeURIComponent(escape(window.atob(
        str.substring(1)))));
  } else {
    return JSON.parse(window.atob(str));
  }
}

/**
 * Mutes the latest checkin from the given user.
 */
function muteCheckin(userid) {
  getItem('cache', function(settings) {
    var cache = settings['cache'];
    if (!cache) {
      cache = {}
    }
    if (userid in cache) {
      cache[userid]['muted'] = true;
      setItem({cache: cache});
    }
  });
}

/**
 * Sends API request to like the given checkin.
 */
function likeCheckin(checkinid, opt_callback) {
  var url = 'https://api.foursquare.com/v2/checkins/' + checkinid + '/like';
  xhr(url, function(resp) {
    if (opt_callback) {
      opt_callback();
    }
  }, { method: 'POST' });
}

/**
 * Getter and setter for data persistence. If the Chrome Storage API is
 * supported, use that, otherwise fall back to using localStorage to persist
 * preferences and checkin cache, etc.
 */
// TODO: Using Storage API causes insta-crahes. Always use localStorage... :(
var useLocalStorage = true; // !('storage' in chrome);
if (useLocalStorage) {
  console.log('Using localStorage');
} else {
  console.log('Using Storage API');
}

// Only necessary for localStorage fallback.
var _ALL_KEYS = ['token', 'highwater', 'cache', 'filterFriends', 'filter',
                 'nearby', 'sound', 'comments', 'interval', 'self_id',
                 'self_pic'];

function getItem(_keys, callback) {
  if (useLocalStorage) {
    if (!_keys) {
      _keys = _ALL_KEYS;
    }

    // Convert single key to array to make life easier.
    var isArray = (typeof _keys == 'object' && 'indexOf' in _keys);
    if (!isArray) {
      _keys = [_keys];
    }
    
    results = {};
    for (var i = 0; i < _keys.length; i++) {
      var key = _keys[i];
      var val = window.localStorage.getItem(key);

      // Convert objects, arrays, and booleans to the appropriate type.
      if (val == null) {
        results[key] = null;
      } else if (val[0] === '{' || val[0] === '[') {
        results[key] = JSON.parse(val);
      } else if (val === 'true') {
        results[key] = true;
      } else if (val === 'false') {
        results[key] = false;
      } else {
        results[key] = val;
      }
    }
    callback(results);
  } else {
    chrome.storage.sync.get(_keys, callback);
  }
}

function setItem(obj) {
  if (useLocalStorage) {
    for (key in obj) {
      var val = obj[key];
      if (typeof val == 'object') val = JSON.stringify(val);
      window.localStorage.setItem(key, val);
    }
  } else {
    chrome.storage.sync.set(obj, function(err) {
      if (err != null) {
        alert('error setting');
        console.error(err);
      }
    });
  }
}

function clearStorage() {
  if (useLocalStorage) {
    window.localStorage.clear();
  } else {
    chrome.storage.sync.clear();
  }
}